/*
 *    Copyright (c) 2014 Xiang Xu <xuxiang@mail.bnu.edu.cn>
 *
 *    Permission is hereby granted, free of charge, to any person
 *    obtaining a copy of this software and associated documentation
 *    files (the "Software"), to deal in the Software without
 *    restriction, including without limitation the rights to use,
 *    copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the
 *    Software is furnished to do so, subject to the following
 *    conditions:
 *
 *    The above copyright notice and this permission notice shall be
 *    included in all copies or substantial portions of the Software.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
 */

#include "arguments.h"
#include "rapidjson/reader.h"
#include "rapidjson/filestream.h"
#include "rapidjson/document.h"
#include <fstream>

Arguments::Arguments()
{
    // Initialize some parameters
    episodes_ = 5000;
    g_episodes_ = 1000;

    resolution_ = 0.0;

    load_qtable_ = false;
    load_svm_model_ = false;
    state_grouping_ = false;

}

Status Arguments::SaveSpatialScale(double xmin, double ymin, double zmin, double xmax, double ymax, double
                                   zmax)
{
    std::ofstream ofs(spatial_scale_.c_str());

    if(!ofs.is_open())
        return Status::OpenFileFail();

    ofs << "min_x" << "\t" << xmin << std::endl;
    ofs << "min_y" << "\t" << ymin << std::endl;
    ofs << "min_z" << "\t" << zmin << std::endl;
    ofs << "range_x" << "\t" << xmax - xmin << std::endl;
    ofs << "range_y" << "\t" << ymax - ymin << std::endl;
    ofs << "range_z" << "\t" << zmax - zmin << std::endl;
    ofs.close();

    return Status::Success();
}

Status Arguments::Parse(int argc, char** argv)
{
    if(!ParseCommandLine(argc, argv))
    {
        return Status("Parsing command line arguments fail");
    }

    // 约定的文件名
    grammar_file_ = working_folder_ + "/grammar.json";
    parameter_file_ = working_folder_ + "/configure.json";
    spatial_scale_ = working_folder_ + "/grid_sacle.txt";
    grid_ = working_folder_ + "/grid_";
    svm_model_file_ = working_folder_ + "/svm.model";
    q_file_ = working_folder_ + "/qtable.txt";
    action_parameters_file_ = working_folder_ + "/action.txt";
    result_png_ = working_folder_ + "/result.png";
    result_txt_ = working_folder_ + "/result.txt";
    original_result_overlap_png_ = working_folder_ + "/compare_result.png";
    raw_pcd_ = working_folder_ + "/raw.pcd";
    pt_pcd_ = working_folder_ + "/pt.pcd";
    rt_pcd_ = working_folder_ + "/rt.pcd";

    // 读取可以设置的变量
    return ReadParameters();
}

bool Arguments::ParseCommandLine(int argc, char** argv)
{
    boost::program_options::options_description options_desc("Parsing Building Facades Tools Options");
    options_desc.add_options()
    ("help", "print help information.")
    ("workplace", boost::program_options::value<std::string>(), "working folder.")
    ;

    boost::program_options::variables_map variables_map;

    try
    {
        boost::program_options::store(boost::program_options::parse_command_line(argc, argv, options_desc),
                                      variables_map);
        boost::program_options::notify(variables_map);

        if(variables_map.count("help") || variables_map.size() == 0)
        {
            std::cout << options_desc << std::endl;
            return false;
        }

        if(variables_map.count("workplace"))
        {
            working_folder_ = variables_map["workplace"].as<std::string>();
        }
        else
        {
            return false;
        }
    }
    catch(boost::program_options::error& e)
    {
        std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
        std::cerr << options_desc << std::endl;
        return false;
    }

    return true;
}

Status Arguments::ReadParameters()
{
    rapidjson::Document document;
    FILE* fp = fopen(parameter_file_.c_str(), "r");

    if(fp == NULL)
    {
        return Status("Open parameter file " + parameter_file_ + " fail");
    }

    rapidjson::FileStream is(fp);

    if(document.ParseStream<0>(is).HasParseError())
    {
        return Status(parameter_file_ + " is not defined properly");
    }

    if(!document.IsObject())
    {
        return Status(parameter_file_ + " is not defined properly");
    }

    // topcd parameters
    if(document.HasMember("raw_format"))
    {
        raw_file_ = working_folder_ + "/" + document["raw_format"].GetString();
    }

    // sample parameters
    if(document.HasMember("resample"))
    {
        const rapidjson::Value& resample = document["resample"];

        if(resample.IsArray())
        {
            for(rapidjson::SizeType i = 0; i < resample.Size(); ++i)
            {
                leaf_size_.push_back(resample[i].GetDouble());
            }
        }
    }

    // outlier parameters
    if(document.HasMember("radius_outlier_remover"))
    {
        const rapidjson::Value& radius_outlier_remover = document["radius_outlier_remover"];

        if(radius_outlier_remover.IsArray())
        {
            for(rapidjson::SizeType i = 0; i < radius_outlier_remover.Size(); ++i)
            {
                radius_outlier_remover_.push_back(radius_outlier_remover[i].GetDouble());
            }
        }
    }

    if(document.HasMember("statistic_outlier_remover"))
    {
        const rapidjson::Value& static_outlier_remover = document["statistic_outlier_remover"];

        if(static_outlier_remover.IsArray())
        {
            for(rapidjson::SizeType i = 0; i < static_outlier_remover.Size(); ++i)
            {
                statiscal_outlier_remover_.push_back(static_outlier_remover[i].GetDouble());
            }
        }
    }

    // transform parameter
    if(document.HasMember("plane_distance"))
    {
        plane_distance_threshold_ = document["plane_distance"].GetDouble();
    }
    else
    {
        plane_distance_threshold_ = -1;
    }

    // feature parameter
    if(document.HasMember("resolution"))
    {
        resolution_ = document["resolution"].GetDouble();
    }

    // classify.exe parameter
    if(document.HasMember("load_svm_model"))
    {
        load_svm_model_ = document["load_svm_model"].GetInt();
    }


    // parsing parameter
    if(document.HasMember("load_qtable"))
    {
        load_qtable_ = document["load_qtable"].GetInt();
    }

    if(document.HasMember("episodes"))
    {
        episodes_ = document["episodes"].GetInt();
    }

    if(document.HasMember("g_episodes"))
    {
        g_episodes_ = document["g_episodes"].GetInt();
    }

    if(document.HasMember("learning_rate"))
    {
        learnning_rate_ = document["learning_rate"].GetDouble();
    }

    if(document.HasMember("grouping"))
    {
        state_grouping_ = document["grouping"].GetInt();
    }
    else
    {
        std::cout << "Warning: State Grouping Not Defined!\n";
    }

    if(document.HasMember("symbol_weights"))
    {
        const rapidjson::Value& weights = document["symbol_weights"];

        if(weights.IsArray())
        {
            for(rapidjson::SizeType i = 0; i < weights.Size(); ++i)
            {
                weights_.push_back(weights[i].GetDouble());
            }
        }
    }

    return Status::Success();
}
